/*
 *
 * Copyright (c) 2005 The Exceoon Software Foundation.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution,
 *    if any, must include the following acknowledgment:
 *       "This product includes software developed by the
 *        Exceoon Software Foundation (http://www.exceoon.com/)."
 *    Alternately, this acknowledgment may appear in the software itself,
 *    if and wherever such third-party acknowledgments normally appear.
 *
 * 4. Products derived from this software may not be called " Exceoon ",
 *    nor may " Exceoon " appear in their name, without prior written
 *    permission of the Exceoon Software Foundation.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * Exceoon Software Corporation and was originally based on software
 * Exceoon copyright (c) 2005, International
 * Business Machines, Inc., http://www.Exceoon.com.  For more
 * information on the Exceoon Software Foundation, please see
 * <http://www.Exceoon.com/>.
 */

package com.exceoon.equals.formula;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.exceoon.equals.formula.support.DataFieldValueContext;
import com.exceoon.equals.formula.support.RelatedFieldContext;
import com.exceoon.equals.formula.support.RelatedFieldInfo;
import com.exceoon.equals.formula.support.VariableContext;

import fr.expression4j.basic.MathematicalElement;
import fr.expression4j.core.Expression;

/**
 * Author	David.Liu 
 * Mail		david.liu@exceoon.com
 * copyright	Exceoon corporation
 */

public class Formula
{
	private String v;
	private String[] varnames;

	private Map<String, Expression> returns = new HashMap<String, Expression>();
	private Map<String, Expression> variables = new HashMap<String, Expression>();
	private List<String> variablenames = new ArrayList<String>();
	private List<RelatedFieldInfo> relatedFieldInfo;

	Formula(String v, String[] varnames)
	{
		this.v = v;
		this.varnames = varnames;
	}

	void parseExpression() throws FormulaParsingExcption
	{
		VariableContext.getContext().clear();
		RelatedFieldContext.getContext().clear();

		for (String var : varnames)
		{
			VariableContext.getContext().addVariableDefinition(var);
		}

		try
		{
			String[] str = v.split(";");

			for (int ind = 0; ind < str.length; ind++)
			{
				if (str[ind].trim().length() != 0)
				{
					System.out.println(str[ind]);

					Expression exp = null;
					String name = null;
					String expStr = null;

					if (str[ind].indexOf(":=") <= 0)
					{
						if (str[ind].indexOf(":") > 0)
						{
							name = str[ind].substring(0, str[ind].indexOf(":"));
							int endindx = str[ind].indexOf("::");
							expStr = str[ind]
							        .substring(str[ind].indexOf(":") + 1,
							                endindx > 0 ? endindx : (str[ind]
							                        .length()));

							exp = ExpressionUtils.createExpression(name + "()="
							        + expStr);
							returns.put(name, exp);
							// TODO support other properties for example
							// COLORSTICK color
						}
					} else
					{
						name = str[ind].substring(0, str[ind].indexOf(":="));
						expStr = str[ind].substring(str[ind].indexOf(":=") + 2,
						        str[ind].length());

						exp = ExpressionUtils.createExpression(name + "()="
						        + expStr);
					}

					if (exp != null)
					{
						// it is a intermediate variable for result.
						VariableContext.getContext()
						        .addVariableDefinition(name);

						variablenames.add(name);
						variables.put(name, exp);
					}
				}
			}

			relatedFieldInfo = RelatedFieldContext.getContext()
			        .getRelatedFields();

			if (relatedFieldInfo == null)
				relatedFieldInfo = new ArrayList<RelatedFieldInfo>(0);

		} 
		catch (Exception e)
		{
			// TODO: handle exception
			throw new IllegalStateException("parse expression error.", e);
		} finally
		{
			VariableContext.getContext().clear();
			RelatedFieldContext.getContext().clear();
		}
	}

	public FormulaResult eval(FormulaParameters p) throws FormulaEvalException
	{
		FormulaResult result = new FormulaResult();
		DataFieldValueContext.getContext().clear();

		for (String varExp : variablenames)
		{
			try
			{
				MathematicalElement m = this.variables.get(varExp).evaluate(
				        p._getParameters());

				p._getParameters().addParameter(varExp, m);

				if (this.returns.containsKey(varExp))
				{
					result._add(varExp, m.getValue());
				}
			} catch (Exception e)
			{
				// TODO: handle exception
				throw new FormulaEvalException("evaluation error", e);
			}
		}

		DataFieldValueContext.getContext().clear();

		return result;
	}

	public List<RelatedFieldInfo> getRelatedFields()
	{
		return relatedFieldInfo;
	}
}
